// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include <zenbase/refcount.h>
#include <zencore/filesystem.h>
#include <zencore/logbase.h>
#include <zencore/uid.h>
#include <zencore/zencore.h>

ZEN_THIRD_PARTY_INCLUDES_START
#include <tsl/robin_map.h>
ZEN_THIRD_PARTY_INCLUDES_END

#include <optional>

namespace zen {

class WorkerThreadPool;
class Workspace;
class WorkspaceShare;

class Workspaces
{
public:
	struct ChunkRequest
	{
		Oid		 ChunkId;
		uint64_t Offset = 0;
		uint64_t Size	= ~uint64_t(0);
	};

	struct ShareFile
	{
		std::string RelativePath;
		uint64_t	Size;
		Oid			Id;
	};

	struct WorkspaceConfiguration
	{
		Oid					  Id;
		std::filesystem::path RootPath;
		inline bool			  operator==(const WorkspaceConfiguration& Rhs) const { return Id == Rhs.Id && RootPath == Rhs.RootPath; }
	};

	struct WorkspaceShareConfiguration
	{
		Oid					  Id;
		std::filesystem::path SharePath;
		std::string			  Alias;
		inline bool			  operator==(const WorkspaceShareConfiguration& Rhs) const
		{
			return Id == Rhs.Id && SharePath == Rhs.SharePath && Alias == Rhs.Alias;
		}
	};

	struct WorkspaceInfo
	{
		WorkspaceConfiguration					 Config;
		std::vector<WorkspaceShareConfiguration> Shares;
	};

	Workspaces();
	~Workspaces();

	bool				   AddWorkspace(const WorkspaceConfiguration& Configuration);
	WorkspaceConfiguration GetWorkspaceConfiguration(const Oid& WorkspaceId) const;
	WorkspaceInfo		   GetWorkspaceInfo(const Oid& WorkspaceId) const;
	bool				   RemoveWorkspace(const Oid& WorkspaceId);

	bool						AddWorkspaceShare(const Oid&												   WorkspaceId,
												  const WorkspaceShareConfiguration&						   Configuration,
												  const std::function<Oid(const std::filesystem::path& Path)>& PathToIdCB);
	WorkspaceShareConfiguration GetWorkspaceShareConfiguration(const Oid& WorkspaceId, const Oid& ShareId) const;
	bool						RemoveWorkspaceShare(const Oid& WorkspaceId, const Oid& ShareId);

	std::optional<std::vector<ShareFile>> GetWorkspaceShareFiles(const Oid&		   WorkspaceId,
																 const Oid&		   ShareId,
																 bool			   ForceRefresh,
																 WorkerThreadPool& WorkerPool);

	ShareFile GetWorkspaceShareChunkInfo(const Oid& WorkspaceId, const Oid& ShareId, const Oid& ChunkId, WorkerThreadPool& WorkerPool);

	std::vector<IoBuffer> GetWorkspaceShareChunks(const Oid&						  WorkspaceId,
												  const Oid&						  ShareId,
												  const std::span<const ChunkRequest> ChunkRequests,
												  WorkerThreadPool&					  WorkerPool);

	void WriteState(const std::filesystem::path& WorkspaceStatePath);
	void ReadState(const std::filesystem::path&									WorkspaceStatePath,
				   const std::function<Oid(const std::filesystem::path& Path)>& PathToIdCB);

	struct ShareAlias
	{
		Oid WorkspaceId;
		Oid ShareId;
	};

	std::optional<ShareAlias> GetShareAlias(std::string_view Alias) const;

private:
	LoggerRef& Log() { return m_Log; }

	Ref<Workspace>									 FindWorkspace(const RwLock::SharedLockScope& Lock, const Oid& WorkspaceId) const;
	std::pair<Ref<Workspace>, Ref<WorkspaceShare>>	 FindWorkspaceShare(const Oid&		  WorkspaceId,
																		const Oid&		  ShareId,
																		bool			  ForceRefresh,
																		WorkerThreadPool& WorkerPool);
	LoggerRef										 m_Log;
	mutable RwLock									 m_Lock;
	tsl::robin_map<Oid, Ref<Workspace>, Oid::Hasher> m_Workspaces;
	tsl::robin_map<std::string, ShareAlias>			 m_ShareAliases;
};

void workspaces_forcelink();

}  // namespace zen
